/*
 * Includes
 */
#include <stdio.h>
#include <stdlib.h>
#include "actions.c"
#include <minix/drivers.h>
#include <minix/driver.h>
#include <minix/safecopies.h>
#include "cs.h"


#define DEV_NAME "cs"
#define OK 0

/*
 * Device functions
 */
FORWARD _PROTOTYPE( char *cs_name, (void) );
FORWARD _PROTOTYPE( int cs_open, (struct driver *d, message *m) );
FORWARD _PROTOTYPE( int cs_close, (struct driver *d, message *m) );
FORWARD _PROTOTYPE( struct device *cs_prepare, (int d) );
FORWARD _PROTOTYPE( int cs_transfer, (endpoint_t endpoint, int opcode, u64_t position, iovec_t *vector, unsigned nr_req, endpoint_t UNUSED(user_endpoint), unsigned int UNUSED(flags)) );
FORWARD _PROTOTYPE( void cs_geometry, (struct partition *entry) );

/*
 * SEF prototypes
 */
FORWARD _PROTOTYPE( void sef_local_startup, (void) );
FORWARD _PROTOTYPE( int sef_cb_init, (int type, sef_init_info_t *info) );

/*
 * Device entrypoint
 */
static struct driver cs_tab =
{
	cs_name,
	cs_open,
	cs_close,
	nop_ioctl,
	cs_prepare,
	cs_transfer,
	nop_cleanup,
	cs_geometry,
	nop_alarm,
	nop_cancel,
	nop_select,
	nop_ioctl,
	do_nop,
};

/*
 * Represents the /dev/cs device
 */
PRIVATE struct device cs_device;

/*
 * Returns the device name
 */
PRIVATE char *cs_name()
{
	return DEV_NAME;
}

/*
 * Opens the device
 */
PRIVATE int cs_open(struct driver *d, message *m)
{
	return OK;
}

/*
 * Close the device
 */
PRIVATE int cs_close(struct driver *d, message *m)
{
	printf("closing the cs driver");
	return OK;
}

/*
 * Prepares the device
 */
PRIVATE struct device *cs_prepare(int d)
{
	cs_device.dv_base.lo = 0;
	cs_device.dv_base.hi = 0;
	cs_device.dv_size.lo = 0;
	cs_device.dv_size.hi = 0;

	return &cs_device;
}

/*
 * Performs the read/write of the device
 */
PRIVATE int cs_transfer(endpoint_t endpoint, int opcode, u64_t position, iovec_t *vector, unsigned nr_req, endpoint_t UNUSED(user_endpoint), unsigned int UNUSED(flags))
{
	/* Variables */
	int bytes, ret;
	char buffer[32], *argument, *arguments[15];
	int argc = 0;
	printf("cs_transfer\n");
	bytes = strlen(buffer) > vector->iov_size ? strlen(buffer) : vector->iov_size;

	if(bytes <= 0)
	{
		return OK;
	} else {
		switch(opcode)
		{
			case DEV_GATHER_S: printf("Entered DEV_GATHER_S\n"); break;
			case DEV_SCATTER_S:
                /* Received data */
				printf("Entered DEV_SCATTER_S from CS\n");
				ret = sys_safecopyfrom(endpoint, vector->iov_addr, 0, (vir_bytes) (buffer), bytes, D);
			break;
			default:
				return EINVAL;
			break;
		}
	}
	return ret;
}

PRIVATE void cs_geometry(struct partition *entry)
{
	entry->cylinders = 0;
	entry->heads 	 = 0;
	entry->sectors   = 0;
}

/*
 * Runs the program
 */
PUBLIC int main(int argc, char* argv[])
{
	message cs_mess;		/* buffer for all incoming messages */
	int r,i, sendreply, endpoints[NO_RT_DEVICES];
	char buffer[32], *argument, *arguments[15];
    
    /* Variable declaration */
	int arguments_counter = 0;
	endpoints[0] =0;/*cs*/
	endpoints[1] =0;/*com*/
	endpoints[2] =0;/*cl*/

	/* Initialize SEF */
	sef_local_startup();

	/*driver_task(&cs_tab, DRIVER_STD);*/

	while (TRUE) {
		r= sef_receive(ANY, &cs_mess);
		if (r != 0)
			panic("driver_receive failed with: %d", r);

		switch (cs_mess.m_type) {
		case SEND_ENDPOINT:
		{
            /*Only send reply if message is received from pm and not directly*/
			sendreply = cs_mess.m_source == PM_PROC_NR 
                                            ? TRUE
                                            : FALSE;
            /*If send from pm the save the msg endpoint*/
			endpoints[cs_mess.RT_DRIVER_ID] = cs_mess.m_source == PM_PROC_NR
                                                                  ? cs_mess.RT_DRIVER_ENDPT 
                                                                  : cs_mess.m_source;
            /* Reply to initializing driver, make endpoint known */
			if(sendreply){
				int s;
				message m;
                
                /* Set message properties and announce own endpoint */
				m.m_type=SEND_ENDPOINT;
				m.RT_DRIVER_ID=RT_DEV_CS;
				m.RT_DRIVER_ENDPT=0;
				if(OK!= (s=send(endpoints[cs_mess.RT_DRIVER_ID], &m))){
					panic("Did not send endpoint from CS to COM.\n");
				}
			}
		} break;
		case COMMAND_TO_CS: main_action(cs_mess.CS_CMD,cs_mess.CS_ARG,cs_mess.CS_VAL); break;
		default: printf("Warning, communication driver received unexpected request.\n"); break;
		}

	}

	/* Run the main loop */

	return OK;
}

/*
 * Sets the SEF calls
 */
PRIVATE void sef_local_startup()
{
	/* Register init call backs */
	sef_setcb_init_fresh(sef_cb_init);
	sef_setcb_init_lu(sef_cb_init);
	sef_setcb_init_restart(sef_cb_init);

	/* Register live update call backs. */
	sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready);
	sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_standard);

	/* Startup SEF */
	sef_startup();
}

/*
 * Handles all the SEF calls
 */
PRIVATE int sef_cb_init(int type, sef_init_info_t *info)
{
	int do_announce_driver = TRUE;

	switch(type)
	{
		case SEF_INIT_FRESH:
		{
			/* Initialise the driver */
			int s;
			message m;
			m.m_type=PM_DRIVER_INIT;
			m.RT_DRIVER_ID=RT_DEV_CS;
            
            /* Announce endpoint to PM */
			if(OK!= (s=send(PM_PROC_NR, &m))){
				panic("Driver init msg did not send\n");
			}
			printf("%s: driver is initialized.\n", PROGRAM_NAME);
		}
		break;

		case SEF_INIT_LU:
			printf("%s: driver is updated.\n", PROGRAM_NAME);
			do_announce_driver = FALSE;
		break;

		case SEF_INIT_RESTART:
			printf("%s: driver is restarted.\n", PROGRAM_NAME);
		break;
	}

	if (do_announce_driver)
	{
		driver_announce();
	}

	/* Driver initialization successful */
	return OK;
}
